/**
 * Copyright (c) 2010-2011 Pierre Labatut
 *
 * This file is part of Space-Chronicles project.
 *
 * Space-Chronicles is free software: you can redistribute it and/or modify it
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or <http://code.google.com/p/space-chronicles/>.
 */
package space.game.world;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jinngine.math.Matrix3;
import jinngine.math.Matrix4;
import jinngine.math.Vector3;
import jinngine.physics.Body;
import jinngine.physics.Scene;
import jinngine.physics.Trigger;
import jinngine.physics.constraint.Constraint;
import jinngine.physics.force.GeneralizedForce;

import net.java.games.input.Component;
import space.game.WldMaterial;
import space.gl.RenderContext;
import space.input.CtlController;
import space.model.Camera;
import space.model.Drawable;
import space.model.MdlEmpty;
import space.model.MdlObject;

/**
 *
 * @author Pierre LABATUT
 */
public class WldShip extends WldInert {

    final CtlController ctlDevice;
    final Component throttle;
    final Component yaw;
    final Component pitch;
    final Component roll;
    final Component fire;
    final Vector3 force = new Vector3();
    final Vector3 torque = new Vector3();    
    
    private final List<Trail> trails;
    private final static double firePeriod = .2;
    private double remainingTime = 0.;
    //Trail update every .2 secs
    private final static double trailRefreshPeriod = .2;
    double trailRefresh = trailRefreshPeriod; // The first time create the initial point

    public WldShip(final MdlObject shipObj, final Map<String, WldMaterial> matLib, final CtlController ctlDevice) {
        super(shipObj, new HashMap<String, Drawable>(), matLib, "PlayerShip");
        if (shipObj == null) {
            throw new NullPointerException();
        }
        if (matLib == null) {
            throw new NullPointerException();
        }
        if (ctlDevice == null) {
            throw new NullPointerException();
        }
        this.ctlDevice = ctlDevice;
        throttle = ctlDevice.component(Component.Identifier.Axis.SLIDER);
        yaw = ctlDevice.component(Component.Identifier.Axis.X);
        pitch = ctlDevice.component(Component.Identifier.Axis.Y);
        roll = ctlDevice.component(Component.Identifier.Axis.RZ);
        fire = ctlDevice.component(Component.Identifier.Button._0);

        {
            final List<Trail> mutableTrails = new ArrayList<Trail>();
            for (MdlEmpty empty : shipObj.empties()) {
                if (empty.name.startsWith("EngineNozzle")) {
                    final Vector3 nozzlePosition = empty.matrix.multiply(new Vector3());
                    // Jingine translate every geometry so that the the mass center is at the origin
                    // I have the position in mesh space but not in body space
                    Matrix4.multiply(objectToBody, nozzlePosition, nozzlePosition);
                    mutableTrails.add(new Trail(nozzlePosition));
                }
            }
            trails = Collections.unmodifiableList(mutableTrails);
        }
    }

    @Override
    public void draw(RenderContext rc) {
        super.draw(rc);
        for (Trail t : trails) {
            new IllegalArgumentException();
            //   t.draw(rc);
        }
    }

    @Override
    public void update(WldScene scene, double dt) {
        if (scene == null) {
            throw new IllegalArgumentException();
        }



        if (remainingTime > 0.) {
            remainingTime -= dt;
        }

        if (fire.getPollData() > .5) {
            if (remainingTime <= 0.) {
                remainingTime = firePeriod;
                Matrix3 orient = body.getOrientation();
                orient = orient.multiply(new Matrix3(
                        0, 0, 1,
                        0, 1, 0,
                        -1, 0, 0));
                Vector3 position = body.getPosition();
                position =
                        position.add(orient.multiply(Vector3.k().multiply(12.)));

                scene.addActor(new WldLazer(position, orient));
            }
        }
    }

    public Camera buildCamera() {
        return new Camera() {

            public Matrix4 viewpoint() {
                /**
                 * Look at X
                 */
                final Matrix4 res = new Matrix4(
                        0, -1, 0, 0,
                        0, 0, 1, -3,
                        -1, 0, 0, -20,
                        0, 0, 0, 1);

                Matrix4.multiply(res, body.getTransform().inverse(), res);
                return res;
            }

            public double fovy() {
                return 60;
            }

            public double near() {
                return 10.;
            }

            public double far() {
                return 2000.;
            }
        };

    }

    @Override
    public void spawn(Scene scene) {
        super.spawn(scene);

        scene.addTrigger(body, new Trigger() {
            public void start(Body body) {
            }

            public boolean update(Body body, double dt) {
                body.applyGeneralizedForce(force, torque, dt);
                return true;
            }

            public void stop(Body body) {
            }

            public void bodyAttached(Body body) {
            }

            public void bodyDetached(Body body) {
            }

            public void constraintAttached(Body body, Constraint c) {
            }

            public void constraintDetached(Body body, Constraint c) {
            }
        });
        
        /* final Body fixed = new Body("fixed");
        scene.addBody(fixed);
        scene.fixBody(fixed, true);
        // gf = new GeneralizedForce(body, fixed);
        //scene.addConstraint(gf);*/
    }

    public void updateTrail(double dt) {
        /**
         * Refresh trails only every trailRefreshPeriod sec
         */
        trailRefresh += dt;
        if (trailRefresh >= trailRefreshPeriod) {
            trailRefresh -= trailRefreshPeriod;
            for (Trail t : trails) {
                t.addTrailPoint(body.getTransform());
            }
        } else {
            for (Trail t : trails) {
                t.overwriteTrailPoint(body.getTransform());
            }
        }
    }

    public void update(double dt) {
        body.deltavelocity.assignZero();
        body.deltaomega.assignZero();
        body.externaldeltavelocity.assignZero();
        body.externaldeltaomega.assignZero();

        double thrust = throttle.getPollData() + 1. - .1;
        if (thrust < 0.) {
            thrust = 0.;
        }

        //Matrix3
        Matrix3 o = body.getOrientation();
        Matrix3.multiply(o, new Vector3(thrust * 40000., 0., 0.), force);
        force.assignAdd(body.getVelocity().multiply(-200));

        Matrix3.multiply(o,
                new Vector3(
                roll.getPollData() * 1000.,
                -pitch.getPollData() * 1000.,
                -yaw.getPollData() * 1000.), torque);
        final Vector3 angularVelocity = body.getAngularVelocity();
        torque.assignAdd(angularVelocity.multiply(-200 * angularVelocity.dot(angularVelocity)));
    }
}
